/**
 * Semihosting系统Call实现
 * 完全Not依赖硬件UART！
 */

#include <sys/stat.h>
#include <stdint.h>

// Semihosting系统Call号
#define SYS_WRITEC  0x03
#define SYS_WRITE0  0x04
#define SYS_READC   0x07

// SemihostingCall
static inline int __attribute__((always_inline))
semihosting_call(int reason, void* arg) {
    register int r0 __asm__("r0") = reason;
    register void* r1 __asm__("r1") = arg;
    
    __asm__ volatile (
        "bkpt #0xAB"
        : "+r" (r0)
        : "r" (r1)
        : "memory"
    );
    
    return r0;
}

// Semihosting exit: request host to terminate the simulation
// SYS_EXIT = 0x18, argument is the exit status code
void semihosting_exit(int code) {
    // Cast code to pointer-sized value for the semihosting call
    (void)semihosting_call(0x18, (void*)(uintptr_t)code);
}

// 堆管理
extern char _end;
static char *heap_end = 0;

void *_sbrk(int incr) {
    char *prev_heap_end;
    
    if (heap_end == 0) {
        heap_end = &_end;
    }
    
    prev_heap_end = heap_end;
    heap_end += incr;
    
    return (void *)prev_heap_end;
}

// Semihosting write
int _write(int file, char *ptr, int len) {
    (void)file;
    
    // UseSYS_WRITE0 - Write以null结尾的String
    // Note：Require逐字符Write
    for (int i = 0; i < len; i++) {
        semihosting_call(SYS_WRITEC, &ptr[i]);
    }
    
    return len;
}

// Semihosting read - 非阻塞Version
int _read(int file, char *ptr, int len) {
    (void)file;
    
    // 非阻塞Read：If没HasData立i.e.Return0
    // 而NotYesNo限Waiting
    
    for (int i = 0; i < len; i++) {
        int c = semihosting_call(SYS_READC, NULL);
        
        // QEMU semihosting可能Return-1表示NoData
        if (c < 0 || c > 255) {
            // 非阻塞：立i.e.Return已Read的字节数
            return i;  // Return0表示NoData，>0表示HasPartialData
        }
        
        ptr[i] = (char)c;
        
        // 遇To换行符就Return
        if (ptr[i] == '\n' || ptr[i] == '\r') {
            if (i + 1 < len) {
                ptr[i+1] = '\0';
            }
            return i + 1;
        }
    }
    
    return len;
}

int _close(int file) {
    (void)file;
    return -1;
}

int _fstat(int file, struct stat *st) {
    (void)file;
    st->st_mode = S_IFCHR;
    return 0;
}

int _isatty(int file) {
    (void)file;
    return 1;
}

int _lseek(int file, int ptr, int dir) {
    (void)file;
    (void)ptr;
    (void)dir;
    return 0;
}
